/*
 * Copyright (c) 2010, Jim Hollinger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Jim Hollinger nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef COMMON_KMLIO_KMLREADER_H_
#define COMMON_KMLIO_KMLREADER_H_

#include <string>
#include <vector>
#include "expat.h"

namespace kmlio {

void XMLCALL xml_startElementHandler(void *userData,
                                     const char *name,
                                     const char **atts);
void XMLCALL xml_endElementHandler(void *userData,
                                   const char *name);
void XMLCALL xml_characterDataHandler(void *userData,
                                      const char *s, int len);

/**
 ****************************************************************************
 * @brief  A class to facilitate reading KML files.
 *
 *  A typical use of this class is shown below.
 *   \code
 *    kmlio::KmlReader kmlReader;
 *    kmlReader.parse("example.kml");
 *    bool continue_flag = true;
 *    while (continue_flag) {
 *        double latitude_degN  = 0.0;
 *        double longitude_degE = 0.0;
 *        double altitude_m     = 0.0;
 *        bool   altitudeValid_flag = false;
 *        continue_flag = kmlReader.getNextCoordinate(latitude_degN, longitude_degE, altitude_m, altitudeValid_flag);
 *        if (continue_flag) {
 *            if (altitudeValid_flag) {
 *                printf("lat/lon: %.6lf/%.6lf deg;  alt: %.1f m\n", latitude_degN, longitude_degE, altitude_m);
 *            } else {
 *                printf("lat/lon: %.6lf/%.6lf deg\n", latitude_degN, longitude_degE);
 *            }
 *        }
 *    }
 *   \endcode
 *  The above code will parse the following input;
 *   \code
 *    <?xml version="1.0" encoding="UTF-8"?>
 *    <kml xmlns="http://www.opengis.net/kml/2.2">
 *      <Document>
 *        <name>Example</name>
 *        <description>Example line string with one point</description>
 *        <Style id="transPurpleLineGreenPoly">
 *          <LineStyle>
 *            <color>7fff00ff</color>
 *            <width>4.0</width>
 *          </LineStyle>
 *          <PolyStyle>
 *            <color>7f00ff00</color>
 *          </PolyStyle>
 *        </Style>
 *        <Placemark>
 *          <styleUrl>#transPurpleLineGreenPoly</styleUrl>
 *          <visibility>1</visibility>
 *          <LineString>
 *            <altitudeMode>clampToGround</altitudeMode>
 *            <tessellate>1</tessellate>
 *            <coordinates>
 *              -80.437700000000,37.213600000000
 *              -80.437600000000,37.213600000000
 *              -80.437600000000,37.213700000000
 *              -80.437700000000,37.213600000000
 *            </coordinates>
 *          </LineString>
 *        </Placemark>
 *      </Document>
 *    </kml>
 *   \endcode
 *  and generate the following output.
 *   \code
 *    lat/lon: 37.213700/-80.437600 deg
 *    lat/lon: 37.213600/-80.437600 deg
 *    lat/lon: 37.213600/-80.437700 deg
 *    lat/lon: 37.213700/-80.437600 deg
*   \endcode
 ****************************************************************************
**/
class KmlReader {
  public:
    friend void XMLCALL xml_startElementHandler(void *userData,
                                                const char *name,
                                                const char **atts);
    friend void XMLCALL xml_endElementHandler(void *userData,
                                              const char *name);
    friend void XMLCALL xml_characterDataHandler(void *userData,
                                                 const char *s, int len);

  protected:
    struct Coordinate {
        double  latitude_degN;
        double  longitude_degE;
        double  altitude_m;
        bool    altitudeValid_flag;
    };

  public:
    KmlReader();
    ~KmlReader();

    bool parse(const std::string &filename);
    bool getNextCoordinate(
        double *latitude_degN,
        double *longitude_degE,
        double *altitude_m,
        bool   *altitudeValid_flag);

    static bool unitTest();

  public:
    int         verbose;
    bool        loopCoordinates_flag;   ///< When getting coordinates,
                                        ///  will loop back to the begining
                                        ///  after last coordinate if true

    static const int MAX_BUFFER_LEN;

  protected:
    void startElementHandler(const char *name, const char **atts);
    void endElementHandler(const char *name);
    void characterDataHandler(const char *s, int len);
    void lineHandler(const char *line);

  protected:
    XML_Parser  parser;
    char       *buffer;
    char       *buffer2;
    int         buffer2len;
    std::vector<std::string> stack;     ///< The XML element stack
    std::vector<Coordinate>  coordinates;
    unsigned int coordinates_index;
};

}  // namespace kmlio

#endif  // COMMON_KMLIO_KMLREADER_H_
